/*
 * Copyright (C) 2008 GeoScheduler Team, as stated on <http://www.geoscheduler.org/authors>.
 * 
 * This file is part of GeoScheduler.
 * 
 * GeoScheduler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GeoScheduler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with GeoScheduler. If not, see <http://www.gnu.org/licenses/>.
 */
package org.geoscheduler.commons.marshaller.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.geoscheduler.commons.marshaller.JSONMarshaller;
import org.geoscheduler.model.Account;
import org.geoscheduler.model.Event;
import org.geoscheduler.model.Location;
import org.geoscheduler.model.Token;
import org.geoscheduler.model.Account.AccountType;
import org.geoscheduler.model.Event.AlertType;
import org.geoscheduler.model.Event.EventType;
import org.geoscheduler.model.Event.RepeatType;
import org.geoscheduler.model.dto.EventQueryDTO;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Naive implementation of {@link JSONMarshaller} - this is only temporary solution. It replaces
 * {@link JSONMarshallerImpl} until reflection on runtime annotations works in Android.
 *
 * @author srakyi
 */
public class NaiveJSONMarshaller implements JSONMarshaller {

	/**
	 * @see org.geoscheduler.commonsOLD.marshaller.JSONMarshaller#marshall(java.lang.Object, org.json.JSONObject)
	 */
	public JSONObject marshall(Object instance, JSONObject jsonObj) throws JSONException {
		if (instance instanceof Account) {
			Account acc = (Account)instance;
			
			jsonObj.put("login", acc.getLogin());
			jsonObj.put("passwd", acc.getPasswd());
			jsonObj.put("accountTypeEnum", acc.getAccountTypeEnum().name());
			return jsonObj;
		} else if (instance instanceof EventQueryDTO) {
			EventQueryDTO eqd = (EventQueryDTO)instance;
			
			jsonObj.put("lat", eqd.getLat());
			jsonObj.put("lon", eqd.getLon());
			jsonObj.put("timeFrom", eqd.getTimeFrom().getTime());
			jsonObj.put("timeTo", eqd.getTimeTo().getTime());
			// FIXME other properties as soon as needed
			return jsonObj;
		}
		
		throw new JSONException("Marshaller for type '" + instance.getClass().getName() + "' not defined!");
	}

	/**
	 * @see org.geoscheduler.commonsOLD.marshaller.JSONMarshaller#unmarshall(java.lang.Object, org.json.JSONObject)
	 * 
	 * FIXME type safety!
	 */
	public <T> T unmarshall(T instance, JSONObject jsonObj) throws JSONException {
		if (instance instanceof Token) {
			Token token = (Token)instance;

			token.setIdAccount(jsonObj.getLong("idAccount"));
			token.setTokenId(jsonObj.getString("tokenId"));
			token.setValidUntil(new Date(Long.parseLong(jsonObj.getString("validUntil"))));
			return (T)token;
		} else if (instance instanceof Account) {
			Account acc = (Account)instance;
			
			acc.setId(jsonObj.getLong("id"));
			acc.setCreated(new Date(jsonObj.getLong("created")));
			acc.setLogin(jsonObj.getString("login"));
			acc.setAccountTypeEnum(AccountType.valueOf(jsonObj.getString("accountTypeEnum")));
			if ((jsonObj.getString("accessed") != null) && (!"null".equals(jsonObj.getString("accessed")))) {
				acc.setAccessed(new Date(jsonObj.getLong("accessed")));
			}
			return (T)acc;
		} else if (instance instanceof Event) {
			Event event = (Event)instance;
			
			// retrieved id is set as remoteId (locally stored events have differend ids)
			if (jsonObj.has("id") && !"null".equals(jsonObj.getString("id"))) {event.setRemoteId(jsonObj.getInt("id"));}
			if (jsonObj.has("eventTypeEnum") && !"null".equals(jsonObj.getString("eventTypeEnum"))) {event.setEventTypeEnum(EventType.valueOf(jsonObj.getString("eventTypeEnum")));}
			if (jsonObj.has("timeCreated") && !"null".equals(jsonObj.getString("timeCreated"))) {event.setTimeCreated(new Date(jsonObj.getLong("timeCreated")));}
			if (jsonObj.has("timeFrom") && !"null".equals(jsonObj.getString("timeFrom"))) {event.setTimeFrom(new Date(jsonObj.getLong("timeFrom")));}
			if (jsonObj.has("timeTo") && !"null".equals(jsonObj.getString("timeTo"))) {event.setTimeTo(new Date(jsonObj.getLong("timeTo")));}
			if (jsonObj.has("lastAlert") && !"null".equals(jsonObj.getString("lastAlert"))) {event.setLastAlert(new Date(jsonObj.getLong("lastAlert")));}
			if (jsonObj.has("alertTypeEnum") && !"null".equals(jsonObj.getString("alertTypeEnum"))) {event.setAlertTypeEnum(AlertType.valueOf(jsonObj.getString("alertTypeEnum")));}
			if (jsonObj.has("repeatTypeEnum") && !"null".equals(jsonObj.getString("repeatTypeEnum"))) {event.setRepeatTypeEnum(RepeatType.valueOf(jsonObj.getString("repeatTypeEnum")));}
			if (jsonObj.has("lastAlertLon") && !"null".equals(jsonObj.getString("lastAlertLon"))) {event.setLastAlertLon(jsonObj.getInt("lastAlertLon"));}
			if (jsonObj.has("lastAlertLat") && !"null".equals(jsonObj.getString("lastAlertLat"))) {event.setLastAlertLat(jsonObj.getInt("lastAlertLat"));}
			if (jsonObj.has("properties") && !"null".equals(jsonObj.getString("properties"))) {event.setProperties(jsonObj.getString("properties"));}
			if (jsonObj.has("title") && !"null".equals(jsonObj.getString("title"))) {event.setTitle(jsonObj.getString("title"));}
			if (jsonObj.has("description") && !"null".equals(jsonObj.getString("description"))) {event.setDescription(jsonObj.getString("description"));}
			if (jsonObj.has("location") && !"null".equals(jsonObj.getString("location"))) {event.setLocation(unmarshall(new Location(), jsonObj.getJSONObject("location")));}
			// FIXME unmarshall tags!
			return (T)event;
		} else if (instance instanceof Location) {
			Location loc = (Location)instance;
			
			// retrieved id is set as remoteId (locally stored locations doesn't have ids)
			if (jsonObj.has("id") && !"null".equals(jsonObj.getString("id"))) {loc.setRemoteId(jsonObj.getInt("id"));}
			if (jsonObj.has("lat") && !"null".equals(jsonObj.getString("lat"))) {loc.setLat(jsonObj.getInt("lat"));}
			if (jsonObj.has("lon") && !"null".equals(jsonObj.getString("lon"))) {loc.setLon(jsonObj.getInt("lon"));}
			if (jsonObj.has("radius") && !"null".equals(jsonObj.getString("radius"))) {loc.setRadius(jsonObj.getInt("radius"));}
			
			return (T)loc;
		}
		
		throw new JSONException("Unmarshaller for type '" + instance.getClass().getName() + "' not defined!");
	}

	/**
	 * @see org.geoscheduler.commons.marshaller.JSONMarshaller#unmarshall(java.lang.Object, org.json.JSONArray)
	 */
	@Override
	public <T> List<T> unmarshall(T instance, JSONArray arr) throws JSONException {
		List<T> items = new ArrayList<T>(2);
		
		try {
			for (int k = 0; k < arr.length(); k ++) {
				items.add(unmarshall((T)instance.getClass().newInstance(), arr.getJSONObject(k)));
			}
		} catch (IllegalAccessException e) {
			throw new JSONException("JSONArray unmarshalling failed. Exception raised - " + e);
		} catch (InstantiationException e) {
			throw new JSONException("JSONArray unmarshalling failed. Exception raised - " + e);
		}
		
		return items;
	}

	
}
